package sms

import (
	"fmt"
	"math/rand"
	"regexp"
	"strings"
	"time"
	"pd/server/plugin/cache"
	"pd/server/plugin/logger"

	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111"
)

const (
	Endpoint       = "sms.tencentcloudapi.com"
	Region         = "ap-guangzhou"
	CacheKeyPrefix = "smscode_"
)

func NewSmsService(secretId string, secretKey string, sdkAppId string, signName string, templateId string) *Sms {
	return &Sms{
		SecretId:  secretId,
		SecretKey: secretKey,

		SdkAppId:   sdkAppId,
		SignName:   signName,
		TemplateId: templateId,
	}
}

type Sms struct {
	SecretId  string // SecretId
	SecretKey string // SecretKey

	SdkAppId   string // 短信应用ID
	SignName   string // 签名信息
	TemplateId string // 模板 ID
}

func (s Sms) SendCode(phone, code string) error {
	credential := common.NewCredential(
		s.SecretId,
		s.SecretKey,
	)
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = Endpoint
	client, _ := sms.NewClient(credential, Region, cpf)

	request := sms.NewSendSmsRequest()

	/* 基本类型的设置:
	 * SDK采用的是指针风格指定参数，即使对于基本类型你也需要用指针来对参数赋值。
	 * SDK提供对基本类型的指针引用封装函数
	 * 帮助链接：
	 * 短信控制台: https://console.cloud.tencent.com/smsv2
	 * sms helper: https://cloud.tencent.com/document/product/382/3773 */
	/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId，示例如1400006666 */
	request.SmsSdkAppId = common.StringPtr(s.SdkAppId)
	/* 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名，签名信息可登录 [短信控制台] 查看 */
	request.SignName = common.StringPtr(s.SignName)
	/* 国际/港澳台短信 SenderId: 国内短信填空，默认未开通，如需开通请联系 [sms helper] */
	request.SenderId = common.StringPtr("")
	/* 用户的 session 内容: 可以携带用户侧 ID 等上下文信息，server 会原样返回 */
	request.SessionContext = common.StringPtr("")
	/* 短信码号扩展号: 默认未开通，如需开通请联系 [sms helper] */
	request.ExtendCode = common.StringPtr("0")
	/* 模板参数: 若无模板参数，则设置为空*/
	request.TemplateParamSet = common.StringPtrs([]string{code})
	/* 模板 ID: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看 */
	request.TemplateId = common.StringPtr(s.TemplateId)
	/* 下发手机号码，采用 E.164 标准，+[国家或地区码][手机号]
	 * 示例如：+8613711112222， 其中前面有一个+号 ，86为国家码，13711112222为手机号，最多不要超过200个手机号*/
	request.PhoneNumberSet = common.StringPtrs([]string{"+86" + phone})
	// 通过client对象调用想要访问的接口，需要传入请求对象

	response, err := client.SendSms(request)
	if _, ok := err.(*errors.TencentCloudSDKError); ok {
		logger.Errorf("An API error has returned: %s", err)
		return err
	}

	if err != nil {
		logger.Errorf("An API error has returned: %s", err)
		return err
	}

	if *response.Response.SendStatusSet[0].Code != "Ok" {
		logger.Errorf("SendCode [%v]", response.ToJsonString())
		return fmt.Errorf("%s", *response.Response.SendStatusSet[0].Message)
	}

	logger.Infof("SendCode [%v]", response.ToJsonString())
	return nil
}

// 校验验证码
func CheckSmsCaptcha(phone string, captcha string) (bool, error) {
	client := cache.NewRedisClient()
	key := fmt.Sprintf("%s%s", CacheKeyPrefix, phone)
	value, err := client.Get(key)
	if err != nil {
		return false, err
	}
	ok := strings.EqualFold(captcha, value)
	if ok {
		affected, err := client.Del(key)
		if err != nil || affected != 1 {
			logger.Errorf("redis del err [%v], result [%v]", err, affected)
		}
		return ok, nil
	}
	return false, nil
}

func CheckVerifycode(phone string, captcha string) (err error) {
	client := cache.NewRedisClient()
	key := fmt.Sprintf("vfc_%s", phone)
	value, err := client.Get(key)
	if err != nil {
		logger.Error(err)
		err = fmt.Errorf("验证码已过期")
		return err
	}
	if value == captcha {
		client.Del(key)
	} else {
		err = fmt.Errorf("验证码错误")
	}
	return
}

const (
	secretId  = "AKIDZ0DE1f9QTIAyaz93h7UowoQwOfjjdL3Z"
	secretKey = "LLBzpYDlKfngJjvvupeFeqoY3KL8jNo3"
	sdkAppId  = "1400526643"
)

func SendVerifycode(templateId, signName, phone string) error {
	var errHint = fmt.Errorf("验证码发送失败，请稍后重试")

	// 校验手机号
	if ok := checkPhone(phone); !ok {
		logger.Errorf("手机号格式错误 [%s]", phone)
		return nil
	}

	// 校验是否已发送验证码
	client := cache.NewRedisClient()
	key := fmt.Sprintf("vfc_%s", phone)
	key2 := fmt.Sprintf("vfc_%s_ban", phone) // 是否禁止发送
	if value, _ := client.Get(key2); value != "" {
		if value != "" {
			return fmt.Errorf("验证码获取过于频繁")
		}
		return errHint
	}

	// 生成验证码
	captcha := generateCaptcha()

	// 发送验证码
	err := NewSmsService(secretId, secretKey, sdkAppId, signName, templateId).
		SendCode(phone, captcha)

	if err != nil {
		logger.Errorf("SendCode err [%v],  phone[%s], captcha [%v]", err, phone, captcha)
		return errHint
	}

	// 缓存验证码
	err = client.Set(key2, "T", time.Minute)
	if err != nil {
		logger.Errorf("GetSmsCaptcha err [%v], key [%v]", err, key2)
		return errHint
	}
	err = client.Set(key, captcha, time.Minute*10)
	if err != nil {
		logger.Errorf("GetSmsCaptcha err [%v], key [%v]", err, key2)
		return errHint
	}

	return nil
}

// 生成验证码
func generateCaptcha() string {
	rand.Seed(time.Now().UnixNano())

	return fmt.Sprintf("%06d", rand.Intn(1000000))
}

// 检查手机号
func checkPhone(phone string) bool {
	reg := `^1\d{10}$`
	rgx := regexp.MustCompile(reg)
	return rgx.MatchString(phone)
}
